{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "AN3Ek2S9IIwK"
      },
      "source": [
        "Copyright 2019 Google LLC\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "you may not use this file except in compliance with the License.\n",
        "You may obtain a copy of the License at\n",
        "\n",
        "    https://www.apache.org/licenses/LICENSE-2.0\n",
        "\n",
        "Unless required by applicable law or agreed to in writing, software\n",
        "distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "See the License for the specific language governing permissions and\n",
        "limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kY-P5psVbNUB"
      },
      "source": [
        "## Benchmark Fairness Experiments for Equal Opportunity\n",
        "\n",
        "Requires paths to the appropriate datasets."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "UpWFW27lIxjS"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import\n",
        "from __future__ import division\n",
        "from __future__ import print_function\n",
        "\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import tensorflow as tf\n",
        "from sklearn.model_selection import train_test_split\n",
        "import cPickle\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Kt5ZpMw3JiP7"
      },
      "source": [
        "## Load dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "v_TTNGF7Fnaq"
      },
      "outputs": [],
      "source": [
        "#@title Load Adult dataset\n",
        "\n",
        "CATEGORICAL_COLUMNS = [\n",
        "    'workclass', 'education', 'marital_status', 'occupation', 'relationship',\n",
        "    'race', 'gender', 'native_country'\n",
        "]\n",
        "CONTINUOUS_COLUMNS = [\n",
        "    'age', 'capital_gain', 'capital_loss', 'hours_per_week', 'education_num'\n",
        "]\n",
        "COLUMNS = [\n",
        "    'age', 'workclass', 'fnlwgt', 'education', 'education_num',\n",
        "    'marital_status', 'occupation', 'relationship', 'race', 'gender',\n",
        "    'capital_gain', 'capital_loss', 'hours_per_week', 'native_country',\n",
        "    'income_bracket'\n",
        "]\n",
        "LABEL_COLUMN = 'label'\n",
        "\n",
        "PROTECTED_GROUPS = [\n",
        "    'gender_Female', 'gender_Male', 'race_White', 'race_Black'\n",
        "]\n",
        "\n",
        "\n",
        "def get_adult_data():\n",
        "  train_file = PATH_TO_ADULT_TRAIN_FILE\n",
        "  test_file = PATH_TO_ADULT_TEST_FILE\n",
        "\n",
        "  train_df_raw = pd.read_csv(train_file, names=COLUMNS, skipinitialspace=True)\n",
        "  test_df_raw = pd.read_csv(\n",
        "      test_file, names=COLUMNS, skipinitialspace=True, skiprows=1)\n",
        "\n",
        "  train_df_raw[LABEL_COLUMN] = (\n",
        "      train_df_raw['income_bracket'].apply(lambda x: '\u003e50K' in x)).astype(int)\n",
        "  test_df_raw[LABEL_COLUMN] = (\n",
        "      test_df_raw['income_bracket'].apply(lambda x: '\u003e50K' in x)).astype(int)\n",
        "  # Preprocessing Features\n",
        "  pd.options.mode.chained_assignment = None  # default='warn'\n",
        "\n",
        "  # Functions for preprocessing categorical and continuous columns.\n",
        "  def binarize_categorical_columns(input_train_df,\n",
        "                                   input_test_df,\n",
        "                                   categorical_columns=[]):\n",
        "\n",
        "    def fix_columns(input_train_df, input_test_df):\n",
        "      test_df_missing_cols = set(input_train_df.columns) - set(\n",
        "          input_test_df.columns)\n",
        "      for c in test_df_missing_cols:\n",
        "        input_test_df[c] = 0\n",
        "      train_df_missing_cols = set(input_test_df.columns) - set(\n",
        "          input_train_df.columns)\n",
        "      for c in train_df_missing_cols:\n",
        "        input_train_df[c] = 0\n",
        "      input_train_df = input_train_df[input_test_df.columns]\n",
        "      return input_train_df, input_test_df\n",
        "\n",
        "    # Binarize categorical columns.\n",
        "    binarized_train_df = pd.get_dummies(\n",
        "        input_train_df, columns=categorical_columns)\n",
        "    binarized_test_df = pd.get_dummies(\n",
        "        input_test_df, columns=categorical_columns)\n",
        "    # Make sure the train and test dataframes have the same binarized columns.\n",
        "    fixed_train_df, fixed_test_df = fix_columns(binarized_train_df,\n",
        "                                                binarized_test_df)\n",
        "    return fixed_train_df, fixed_test_df\n",
        "  \n",
        "  def bucketize_continuous_column(input_train_df,\n",
        "                                  input_test_df,\n",
        "                                  continuous_column_name,\n",
        "                                  num_quantiles=None,\n",
        "                                  bins=None):\n",
        "    assert (num_quantiles is None or bins is None)\n",
        "    if num_quantiles is not None:\n",
        "      train_quantized, bins_quantized = pd.qcut(\n",
        "          input_train_df[continuous_column_name],\n",
        "          num_quantiles,\n",
        "          retbins=True,\n",
        "          labels=False)\n",
        "      input_train_df[continuous_column_name] = pd.cut(\n",
        "          input_train_df[continuous_column_name], bins_quantized, labels=False)\n",
        "      input_test_df[continuous_column_name] = pd.cut(\n",
        "          input_test_df[continuous_column_name], bins_quantized, labels=False)\n",
        "    elif bins is not None:\n",
        "      input_train_df[continuous_column_name] = pd.cut(\n",
        "          input_train_df[continuous_column_name], bins, labels=False)\n",
        "      input_test_df[continuous_column_name] = pd.cut(\n",
        "          input_test_df[continuous_column_name], bins, labels=False)\n",
        "\n",
        "  # Filter out all columns except the ones specified.\n",
        "  train_df = train_df_raw[CATEGORICAL_COLUMNS + CONTINUOUS_COLUMNS +\n",
        "                          [LABEL_COLUMN]]\n",
        "  test_df = test_df_raw[CATEGORICAL_COLUMNS + CONTINUOUS_COLUMNS +\n",
        "                        [LABEL_COLUMN]]\n",
        "  # Bucketize continuous columns.\n",
        "  bucketize_continuous_column(train_df, test_df, 'age', num_quantiles=4)\n",
        "  bucketize_continuous_column(\n",
        "      train_df, test_df, 'capital_gain', bins=[-1, 1, 4000, 10000, 100000])\n",
        "  bucketize_continuous_column(\n",
        "      train_df, test_df, 'capital_loss', bins=[-1, 1, 1800, 1950, 4500])\n",
        "  bucketize_continuous_column(\n",
        "      train_df, test_df, 'hours_per_week', bins=[0, 39, 41, 50, 100])\n",
        "  bucketize_continuous_column(\n",
        "      train_df, test_df, 'education_num', bins=[0, 8, 9, 11, 16])\n",
        "  train_df, test_df = binarize_categorical_columns(\n",
        "      train_df,\n",
        "      test_df,\n",
        "      categorical_columns=CATEGORICAL_COLUMNS + CONTINUOUS_COLUMNS)\n",
        "  feature_names = list(train_df.keys())\n",
        "  feature_names.remove(LABEL_COLUMN)\n",
        "  num_features = len(feature_names)\n",
        "  return train_df, test_df, feature_names\n",
        "\n",
        "\n",
        "train_df, test_df, feature_names = get_adult_data()\n",
        "X_train_adult = np.array(train_df[feature_names])\n",
        "y_train_adult = np.array(train_df[LABEL_COLUMN])\n",
        "X_test_adult = np.array(test_df[feature_names])\n",
        "y_test_adult = np.array(test_df[LABEL_COLUMN])\n",
        "\n",
        "protected_train_adult = [np.array(train_df[g]) for g in PROTECTED_GROUPS]\n",
        "protected_test_adult = [np.array(test_df[g]) for g in PROTECTED_GROUPS]\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "n5T11e7ZjyP-"
      },
      "outputs": [],
      "source": [
        "#@title Load Bank dataset\n",
        "\n",
        "FEATURES = [\n",
        "    u'campaign', u'previous', u'emp.var.rate', u'cons.price.idx',\n",
        "    u'cons.conf.idx', u'euribor3m', u'nr.employed', u'job_admin.',\n",
        "    u'job_blue-collar', u'job_entrepreneur', u'job_housemaid',\n",
        "    u'job_management', u'job_retired', u'job_self-employed', u'job_services',\n",
        "    u'job_student', u'job_technician', u'job_unemployed', u'job_unknown',\n",
        "    u'marital_divorced', u'marital_married', u'marital_single',\n",
        "    u'marital_unknown', u'education_basic.4y', u'education_basic.6y',\n",
        "    u'education_basic.9y', u'education_high.school', u'education_illiterate',\n",
        "    u'education_professional.course', u'education_university.degree',\n",
        "    u'education_unknown', u'default_no', u'default_unknown', u'default_yes',\n",
        "    u'housing_no', u'housing_unknown', u'housing_yes', u'loan_no',\n",
        "    u'loan_unknown', u'loan_yes', u'contact_cellular', u'contact_telephone',\n",
        "    u'day_of_week_fri', u'day_of_week_mon', u'day_of_week_thu',\n",
        "    u'day_of_week_tue', u'day_of_week_wed', u'poutcome_failure',\n",
        "    u'poutcome_nonexistent', u'poutcome_success', u'y_yes', u'age_0', u'age_1',\n",
        "    u'age_2', u'age_3', u'age_4', u'duration_0.0', u'duration_1.0',\n",
        "    u'duration_2.0', u'duration_3.0', u'duration_4.0'\n",
        "]\n",
        "features = [\n",
        "    u'campaign', u'previous', u'emp.var.rate', u'cons.price.idx',\n",
        "    u'cons.conf.idx', u'euribor3m', u'nr.employed', u'job_admin.',\n",
        "    u'job_blue-collar', u'job_entrepreneur', u'job_housemaid',\n",
        "    u'job_management', u'job_retired', u'job_self-employed', u'job_services',\n",
        "    u'job_student', u'job_technician', u'job_unemployed', u'job_unknown',\n",
        "    u'marital_divorced', u'marital_married', u'marital_single',\n",
        "    u'marital_unknown', u'education_basic.4y', u'education_basic.6y',\n",
        "    u'education_basic.9y', u'education_high.school', u'education_illiterate',\n",
        "    u'education_professional.course', u'education_university.degree',\n",
        "    u'education_unknown', u'default_no', u'default_unknown', u'default_yes',\n",
        "    u'housing_no', u'housing_unknown', u'housing_yes', u'loan_no',\n",
        "    u'loan_unknown', u'loan_yes', u'contact_cellular', u'contact_telephone',\n",
        "    u'day_of_week_fri', u'day_of_week_mon', u'day_of_week_thu',\n",
        "    u'day_of_week_tue', u'day_of_week_wed', u'poutcome_failure',\n",
        "    u'poutcome_nonexistent', u'poutcome_success', u'age_0', u'age_1',\n",
        "    u'age_2', u'age_3', u'age_4', u'duration_0.0', u'duration_1.0',\n",
        "    u'duration_2.0', u'duration_3.0', u'duration_4.0'\n",
        "] \n",
        "LABEL_COLUMN = [\"y_yes\"]\n",
        "protected_features = ['age_0', 'age_1', 'age_2', 'age_3', 'age_4']\n",
        "\n",
        "\n",
        "def get_data():\n",
        "  data_path = PATH_TO_BANK_DATA\n",
        "  df = pd.read_csv(data_file, sep=';')\n",
        "  continuous_features = [\n",
        "      'campaign', 'previous', 'emp.var.rate', 'cons.price.idx', 'cons.conf.idx',\n",
        "      'euribor3m', 'nr.employed'\n",
        "  ]\n",
        "  continuous_to_categorical_features = ['age', 'duration']\n",
        "  categorical_features = [\n",
        "      'job', 'marital', 'education', 'default', 'housing', 'loan', 'contact',\n",
        "      'day_of_week', 'poutcome', 'y'\n",
        "  ]\n",
        "\n",
        "  # Functions for preprocessing categorical and continuous columns.\n",
        "  def binarize_categorical_columns(input_df, categorical_columns=[]):\n",
        "    # Binarize categorical columns.\n",
        "    binarized_df = pd.get_dummies(input_df, columns=categorical_columns)\n",
        "    return binarized_df\n",
        "\n",
        "  def bucketize_continuous_column(input_df, continuous_column_name, bins=None):\n",
        "    input_df[continuous_column_name] = pd.cut(\n",
        "        input_df[continuous_column_name], bins, labels=False)\n",
        "\n",
        "  for c in continuous_to_categorical_features:\n",
        "    b = [0] + list(np.percentile(df[c], [20, 40, 60, 80, 100]))\n",
        "    bucketize_continuous_column(df, c, bins=b)\n",
        "\n",
        "  df = binarize_categorical_columns(\n",
        "      df,\n",
        "      categorical_columns=categorical_features +\n",
        "      continuous_to_categorical_features)\n",
        "\n",
        "  to_fill = [\n",
        "      u'duration_0.0', u'duration_1.0', u'duration_2.0', u'duration_3.0',\n",
        "      u'duration_4.0'\n",
        "  ]\n",
        "  for i in range(len(to_fill) - 1):\n",
        "    df[to_fill[i]] = df[to_fill[i:]].max(axis=1)\n",
        "\n",
        "  normalize_features = [\n",
        "      'cons.price.idx', 'cons.conf.idx', 'euribor3m', 'nr.employed'\n",
        "  ]\n",
        "  for feature in normalize_features:\n",
        "    df[feature] = df[feature] - np.mean(df[feature])\n",
        "\n",
        "  label = [\"u'y_yes\"]\n",
        "  df = df[FEATURES]\n",
        "\n",
        "  return df\n",
        "\n",
        "\n",
        "df = get_data()\n",
        "\n",
        "y = np.array(df[LABEL_COLUMN]).flatten()\n",
        "\n",
        "X_train_bank, X_test_bank, y_train_bank, y_test_bank = train_test_split(df, y, test_size=0.2, random_state=42)\n",
        "protected_train_bank = [X_train_bank[g] for g in protected_features]\n",
        "protected_test_bank = [X_test_bank[g] for g in protected_features]\n",
        "X_train_bank = np.array(X_train_bank[features])\n",
        "X_test_bank = np.array(X_test_bank[features])\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "USb_xvfFnP_z"
      },
      "outputs": [],
      "source": [
        "#@title Load COMPAS dataset\n",
        "\n",
        "LABEL_COLUMN = 'two_year_recid'\n",
        "PROTECTED_GROUPS = [\n",
        "    'sex_Female', 'sex_Male', 'race_Caucasian', 'race_African-American'\n",
        "]\n",
        "\n",
        "\n",
        "def get_data():\n",
        "  data_path = PATH_TO_COMPAS_DATA\n",
        "  df = pd.read_csv(data_path)\n",
        "  FEATURES = [\n",
        "      'age', 'c_charge_degree', 'race', 'age_cat', 'score_text', 'sex',\n",
        "      'priors_count', 'days_b_screening_arrest', 'decile_score', 'is_recid',\n",
        "      'two_year_recid'\n",
        "  ]\n",
        "  df = df[FEATURES]\n",
        "  df = df[df.days_b_screening_arrest \u003c= 30]\n",
        "  df = df[df.days_b_screening_arrest \u003e= -30]\n",
        "  df = df[df.is_recid != -1]\n",
        "  df = df[df.c_charge_degree != 'O']\n",
        "  df = df[df.score_text != 'N/A']\n",
        "  continuous_features = [\n",
        "      'priors_count', 'days_b_screening_arrest', 'is_recid', 'two_year_recid'\n",
        "  ]\n",
        "  continuous_to_categorical_features = ['age', 'decile_score', 'priors_count']\n",
        "  categorical_features = ['c_charge_degree', 'race', 'score_text', 'sex']\n",
        "\n",
        "  # Functions for preprocessing categorical and continuous columns.\n",
        "  def binarize_categorical_columns(input_df, categorical_columns=[]):\n",
        "    # Binarize categorical columns.\n",
        "    binarized_df = pd.get_dummies(input_df, columns=categorical_columns)\n",
        "    return binarized_df\n",
        "\n",
        "  def bucketize_continuous_column(input_df, continuous_column_name, bins=None):\n",
        "    input_df[continuous_column_name] = pd.cut(\n",
        "        input_df[continuous_column_name], bins, labels=False)\n",
        "\n",
        "  for c in continuous_to_categorical_features:\n",
        "    b = [0] + list(np.percentile(df[c], [20, 40, 60, 80, 90, 100]))\n",
        "    if c == 'priors_count':\n",
        "      b = list(np.percentile(df[c], [0, 50, 70, 80, 90, 100]))\n",
        "    bucketize_continuous_column(df, c, bins=b)\n",
        "\n",
        "  df = binarize_categorical_columns(\n",
        "      df,\n",
        "      categorical_columns=categorical_features +\n",
        "      continuous_to_categorical_features)\n",
        "\n",
        "  to_fill = [\n",
        "      u'decile_score_0', u'decile_score_1', u'decile_score_2',\n",
        "      u'decile_score_3', u'decile_score_4', u'decile_score_5'\n",
        "  ]\n",
        "  for i in range(len(to_fill) - 1):\n",
        "    df[to_fill[i]] = df[to_fill[i:]].max(axis=1)\n",
        "  to_fill = [\n",
        "      u'priors_count_0.0', u'priors_count_1.0', u'priors_count_2.0',\n",
        "      u'priors_count_3.0', u'priors_count_4.0'\n",
        "  ]\n",
        "  for i in range(len(to_fill) - 1):\n",
        "    df[to_fill[i]] = df[to_fill[i:]].max(axis=1)\n",
        "\n",
        "  features = [\n",
        "      u'days_b_screening_arrest', u'c_charge_degree_F', u'c_charge_degree_M',\n",
        "      u'race_African-American', u'race_Asian', u'race_Caucasian',\n",
        "      u'race_Hispanic', u'race_Native American', u'race_Other',\n",
        "      u'score_text_High', u'score_text_Low', u'score_text_Medium',\n",
        "      u'sex_Female', u'sex_Male', u'age_0', u'age_1', u'age_2', u'age_3',\n",
        "      u'age_4', u'age_5', u'decile_score_0', u'decile_score_1',\n",
        "      u'decile_score_2', u'decile_score_3', u'decile_score_4',\n",
        "      u'decile_score_5', u'priors_count_0.0', u'priors_count_1.0',\n",
        "      u'priors_count_2.0', u'priors_count_3.0', u'priors_count_4.0'\n",
        "  ]\n",
        "  label = ['two_year_recid']\n",
        "\n",
        "  df = df[features + label]\n",
        "  return df, features, label\n",
        "\n",
        "df, feature_names, label_column = get_data()\n",
        "\n",
        "from sklearn.utils import shuffle\n",
        "df = shuffle(df, random_state=12345)\n",
        "N = len(df)\n",
        "train_df = df[:int(N * 0.66)]\n",
        "test_df = df[int(N * 0.66):]\n",
        "\n",
        "X_train_compas = np.array(train_df[feature_names])\n",
        "y_train_compas = np.array(train_df[label_column]).flatten()\n",
        "X_test_compas = np.array(test_df[feature_names])\n",
        "y_test_compas = np.array(test_df[label_column]).flatten()\n",
        "\n",
        "protected_train_compas = [np.array(train_df[g]) for g in PROTECTED_GROUPS]\n",
        "protected_test_compas = [np.array(test_df[g]) for g in PROTECTED_GROUPS]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "8mI2pv7Mqwms"
      },
      "outputs": [],
      "source": [
        "#@title Load Crime dataset\n",
        "\n",
        "LABEL_COLUMN = 'label'\n",
        "\n",
        "EXCLUDED_COLUMNS = [\n",
        "    'state', 'county', 'community', 'communityname', 'ViolentCrimesPerPop'\n",
        "]\n",
        "PROTECTED_GROUPS = ['racepctblack_cat_low', 'racepctblack_cat_high',\n",
        "                    'racePctAsian_cat_low', 'racePctAsian_cat_high',\n",
        "                    'racePctWhite_cat_low', 'racePctWhite_cat_high',\n",
        "                    'racePctHisp_cat_low','racePctHisp_cat_high']\n",
        "\n",
        "def _dataframes(data_dir):\n",
        "  \"\"\"Returns the dataframes and feature names.\"\"\"\n",
        "  train_file = os.path.join(data_dir, 'train.csv')\n",
        "  val_file = os.path.join(data_dir, 'val.csv')\n",
        "  test_file = os.path.join(data_dir, 'test.csv')\n",
        "\n",
        "  # Replace all missing feature values with the mean over the training set.\n",
        "  feature_names = [\n",
        "      name for name in train_df.keys()\n",
        "      if name not in [LABEL_COLUMN] + EXCLUDED_COLUMNS\n",
        "  ]\n",
        "  for column in feature_names:\n",
        "    train_mean = train_df[column].mean()\n",
        "    train_df[column].fillna(train_mean, inplace=True)\n",
        "    val_df[column].fillna(train_mean, inplace=True)\n",
        "    test_df[column].fillna(train_mean, inplace=True)\n",
        "\n",
        "  return train_df, val_df, test_df, feature_names\n",
        "\n",
        "train_df, val_df, test_df, feature_names = _dataframes(PATH_TO_CRIME_DATA)\n",
        "train_df = pd.concat((train_df, val_df))\n",
        "X_train_crime = np.array(train_df[feature_names])\n",
        "y_train_crime = np.array(train_df[LABEL_COLUMN]).flatten()\n",
        "X_test_crime = np.array(test_df[feature_names])\n",
        "y_test_crime = np.array(test_df[LABEL_COLUMN]).flatten()\n",
        "\n",
        "protected_train_crime = [np.array(train_df[g]) for g in PROTECTED_GROUPS]\n",
        "protected_test_crime = [np.array(test_df[g]) for g in PROTECTED_GROUPS]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "nmsPQ6NrvXBu"
      },
      "outputs": [],
      "source": [
        "#@title Load German Statlog dataset \n",
        "\n",
        "\n",
        "data_path = PATH_TO_GERMAN_STATLOG\n",
        "with open(data_path, \"rb\") as fp:\n",
        "    X = cPickle.load(fp)\n",
        "    y = cPickle.load(fp)\n",
        "\n",
        "# protected attribute is whether is age\n",
        "X_train_german, X_test_german, y_train_german, y_test_german = train_test_split(X, y, test_size=0.33, random_state=42)\n",
        "protected_train_german = [np.where(X_train_german[:, 9] \u003c= 30, 1, 0)]\n",
        "protected_test_german = [np.where(X_test_german[:, 9] \u003c= 30, 1, 0)]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "cYl7_Y188FsM"
      },
      "source": [
        "## Prepare Data"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "1WkOnsAuI4h8"
      },
      "outputs": [],
      "source": [
        "\n",
        "dataset_names = [\"Adult\", \"Bank\", \"COMPAS\", \"Crime\", \"German Statlog\"]\n",
        "\n",
        "datas = [(X_train_adult, y_train_adult, X_test_adult, y_test_adult, protected_train_adult, protected_test_adult),\n",
        "         (X_train_bank, y_train_bank, X_test_bank, y_test_bank, protected_train_bank, protected_test_bank),\n",
        "         (X_train_compas, y_train_compas, X_test_compas, y_test_compas, protected_train_compas, protected_test_compas),\n",
        "         (X_train_crime, y_train_crime, X_test_crime, y_test_crime, protected_train_crime, protected_test_crime),\n",
        "         (X_train_german, y_train_german, X_test_german, y_test_german, protected_train_german, protected_test_german),]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "fkmnRfsj6_Yz"
      },
      "outputs": [],
      "source": [
        "#@title Helper Functions\n",
        "def get_error_and_violations(y_pred, y, protected_attributes):\n",
        "  acc = np.mean(y_pred != y)\n",
        "  violations = []\n",
        "  for p in protected_attributes:\n",
        "    protected_idxs = np.where(np.logical_and(p \u003e 0, y \u003e 0))\n",
        "    positive_idxs = np.where(y \u003e 0)\n",
        "    violations.append(np.mean(y_pred[positive_idxs]) - np.mean(y_pred[protected_idxs]))\n",
        "  pairwise_violations = []\n",
        "  for i in range(len(protected_attributes)):\n",
        "    for j in range(i+1, len(protected_attributes)):\n",
        "      protected_idxs = np.where(np.logical_and(protected_attributes[i] \u003e 0, protected_attributes[j] \u003e 0))\n",
        "      if len(protected_idxs[0]) == 0:\n",
        "        continue\n",
        "      pairwise_violations.append(np.mean(y_pred) - np.mean(y_pred[protected_idxs]))\n",
        "  return acc, violations, pairwise_violations"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "figp3NbvS7QK"
      },
      "source": [
        "## Logistic Regression on original dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {
          "height": 734
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 670,
          "status": "ok",
          "timestamp": 1539411707162,
          "user": {
            "displayName": "Heinrich Jiang",
            "photoUrl": "",
            "userId": "02010368581707572492"
          },
          "user_tz": 420
        },
        "id": "gIWtoX1mTEXb",
        "outputId": "eb0e64d1-c448-48bb-dfce-a2aac14668dd"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Processing  Adult\n",
            "Train Accuracy 0.859770891557\n",
            "Train Violation 0.117634167714  \t\t All violations [0.093056776546672548, -0.01646861896555496, -0.0067588222483615512, 0.11763416771380275]\n",
            "Train Intersect Violations 0.158277923277  \t All violations [0.11361360164680066, 0.1582779232765294, -0.072077592314821026, 0.073860584934578546]\n",
            "Test Accuracy 0.858485351023\n",
            "Test Violation 0.119537965876  \t\t All violations [0.086236195210520283, -0.015626337584215944, -0.0076106310728074611, 0.11953796587617693]\n",
            "Test Intersect Violations 0.158158349955  \t All violations [0.11115215201775935, 0.15815834995501074, -0.07108345283695916, 0.072908589919246203]\n",
            "\n",
            "\n",
            "Processing  Bank\n",
            "Train Accuracy 0.906798179059\n",
            "Train Violation 0.119987259118  \t\t All violations [-0.038546536336591608, 0.06213278994735949, 0.080963480963480938, 0.1199872591176939, -0.10843584276420098]\n",
            "Test Accuracy 0.905802379218\n",
            "Test Violation 0.145225362872  \t\t All violations [-0.075956555983915763, 0.013190730837789655, 0.094868435032050114, 0.14522536287242166, -0.082047364400305578]\n",
            "\n",
            "\n",
            "Processing  COMPAS\n",
            "Train Accuracy 0.691136754235\n",
            "Train Violation 0.189881765509  \t\t All violations [0.18988176550944463, -0.033380488713762202, 0.14381743707886441, -0.10887713500370222]\n",
            "Train Intersect Violations 0.210802544913  \t All violations [0.21080254491335595, 0.12743529487943922, 0.10710921572983068, -0.19365824572629647]\n",
            "Test Accuracy 0.685088137208\n",
            "Test Violation 0.23729070499  \t\t All violations [0.23729070498972948, -0.039356932909918818, 0.14476474631799102, -0.12460145086510754]\n",
            "Test Intersect Violations 0.26353718769  \t All violations [0.26353718769034074, 0.12354402997566405, 0.080890467322101423, -0.1808030161855414]\n",
            "\n",
            "\n",
            "Processing  Crime\n",
            "Train Accuracy 0.869565217391\n",
            "Train Violation 0.664829866373  \t\t All violations [0.36809784022898778, -0.1164826586372254, 0.01713165665410854, -0.0082306621864711715, -0.14280623154464922, 0.66482986637277863, -0.00075605303435433147, -0.033489461358313766]\n",
            "Train Intersect Violations 0.516806020067  \t All violations [0.21505672503114959, 0.21796409487563911, -0.19635147461234415, 0.25819397993311038, 0.25819397993311038, 0.082137641904941383, -0.4189360649099389, -0.33722586739513388, -0.49871209741495587, -0.32469372060164897, -0.48413730841044794, -0.51680602006688958, 0.25436256230858934, 0.011545186367426741, -0.23160193843423654, -0.30813255067913453, 0.25819397993311038, 0.20980688315891682, -0.13195753521840475, -0.48807467678330757, -0.39035674470457077, 0.25492600607690125, 0.25819397993311038]\n",
            "Test Accuracy 0.88376753507\n",
            "Test Violation 0.551282051282  \t\t All violations [0.24572649572649574, -0.074824143408214194, -0.0095287595287595339, -0.0072285870158209642, -0.11099865047233459, 0.55128205128205132, 0.041282051282051313, -0.038224121557454804]\n",
            "Test Intersect Violations 0.521477738085  \t All violations [0.2779425517702071, 0.16175207557973093, -0.2836260756807733, 0.30460921843687377, 0.30460921843687377, 0.054609218436873774, -0.41449190515863182, -0.29955744822979286, -0.46811805429039893, -0.2953907815631262, -0.41160699777934245, -0.51892019332783201, 0.29178870561636094, -0.0049145910869357601, -0.28998537615772085, -0.33718182633924559, 0.30460921843687377, 0.30460921843687377, -0.16907499208944199, -0.52147773808486542, -0.39632536100237853, 0.29408290264740011, 0.30460921843687377]\n",
            "\n",
            "\n",
            "Processing  German Statlog\n",
            "Train Accuracy 0.782089552239\n",
            "Train Violation 0.0364558642268  \t\t All violations [-0.036455864226794366]\n",
            "Test Accuracy 0.751515151515\n",
            "Test Violation 0.111975483263  \t\t All violations [-0.11197548326261203]\n",
            "\n",
            "\n"
          ]
        }
      ],
      "source": [
        "#@title Run experiment\n",
        "\n",
        "from sklearn.linear_model import LogisticRegression\n",
        "for dataset_idx, dataset_name in enumerate(dataset_names):\n",
        "  print(\"Processing \", dataset_name)\n",
        "  X_train, y_train, X_test, y_test, protected_train, protected_test = datas[dataset_idx]\n",
        "  model = LogisticRegression()\n",
        "  model.fit(X_train, y_train)\n",
        "  y_pred_train = model.predict(X_train)\n",
        "  y_pred_test = model.predict(X_test)\n",
        "\n",
        "  acc, violations, pairwise_violations = get_error_and_violations(y_pred_train, y_train, protected_train)\n",
        "  print(\"Train Accuracy\", acc)\n",
        "  print(\"Train Violation\", max(np.abs(violations)), \" \\t\\t All violations\", violations)\n",
        "  if len(pairwise_violations) \u003e 0:\n",
        "    print(\"Train Intersect Violations\", max(np.abs(pairwise_violations)), \" \\t All violations\", pairwise_violations)\n",
        "\n",
        "  acc, violations, pairwise_violations = get_error_and_violations(y_pred_test, y_test, protected_test)\n",
        "  print(\"Test Accuracy\", acc)\n",
        "  print(\"Test Violation\", max(np.abs(violations)), \" \\t\\t All violations\", violations)\n",
        "  if len(pairwise_violations) \u003e 0:\n",
        "    print(\"Test Intersect Violations\", max(np.abs(pairwise_violations)), \" \\t All violations\", pairwise_violations)\n",
        "  print()\n",
        "  print()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "XaS909wgKpoM"
      },
      "source": [
        "## Data debiasing procedure"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "IeLAmk9OKvck"
      },
      "outputs": [],
      "source": [
        "def debias_weights(original_labels, predicted, protected_attributes, multipliers):\n",
        "  exponents = np.zeros(len(original_labels))\n",
        "  for i, m in enumerate(multipliers):\n",
        "    exponents -= m * protected_attributes[i]\n",
        "  weights = np.exp(exponents)/ (np.exp(exponents) + np.exp(-exponents))\n",
        "  #weights = np.where(predicted \u003e 0, weights, 1 - weights)\n",
        "  weights = np.where(original_labels \u003e 0, 1 - weights, weights)\n",
        "  return weights"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dPwShzeDIbLR"
      },
      "source": [
        "## Our method"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "height": 836
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 46597,
          "status": "ok",
          "timestamp": 1539412417717,
          "user": {
            "displayName": "Heinrich Jiang",
            "photoUrl": "",
            "userId": "02010368581707572492"
          },
          "user_tz": 420
        },
        "id": "Bz-NnoqbEkbC",
        "outputId": "0644130b-4357-4f74-8202-941c83028b6e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Processing  Adult\n",
            "[ 0.21833614 -0.03863979 -0.01807924  0.23354414]\n",
            "Train Accuracy 0.85900310187\n",
            "Train Violation 0.00112902859052  \t\t All violations [-0.00026112713678849708, 4.6212683019186684e-05, 6.1482610411500715e-05, -0.0011290285905234398]\n",
            "Train Intersect Violations 0.130279936614  \t All violations [0.091603198244022174, 0.13027993661365347, -0.056324075987816191, 0.037163506749669006]\n",
            "Test Accuracy 0.855352865303\n",
            "Test Violation 0.00922528521253  \t\t All violations [-0.0036806896004654144, 0.00066695542514572104, -0.0011747403993581651, 0.0092252852125257467]\n",
            "Test Intersect Violations 0.123498634906  \t All violations [0.086740460457961896, 0.12349863490574281, -0.054071182050675848, 0.040328017047144199]\n",
            "\n",
            "\n",
            "Processing  Bank\n",
            "[-0.06107246  0.12154373  0.19560109  0.29810647 -0.28492557]\n",
            "Train Accuracy 0.903915022762\n",
            "Train Violation 0.0115456113539  \t\t All violations [-0.0077944542614504786, 0.011545611353906915, 0.00083697978434821296, 0.0018826330039144468, 9.0352172048946411e-05]\n",
            "Test Accuracy 0.905195435785\n",
            "Test Violation 0.0431524253802  \t\t All violations [-0.025834266053144317, -0.043152425380210135, 0.009639834260013469, 0.029335370511841097, 0.029335370511841097]\n",
            "\n",
            "\n",
            "Processing  COMPAS\n",
            "[ 0.22599589 -0.03972921 -0.06108455 -0.39770964]\n",
            "Train Accuracy 0.661919960717\n",
            "Train Violation 0.0200590387363  \t\t All violations [0.020059038736319812, -0.0035263023510982983, 0.0044887569627084511, -0.0030193122985927578]\n",
            "Train Intersect Violations 0.0506167659985  \t All violations [0.016598315313431145, 0.050616765998548474, 0.016805805716999966, -0.036290229445032351]\n",
            "Test Accuracy 0.663649356837\n",
            "Test Violation 0.0774950542329  \t\t All violations [0.077495054232894434, -0.012853295919983743, -0.0019744636040542329, -0.014298407266026048]\n",
            "Test Intersect Violations 0.0688641953054  \t All violations [0.060927144330332111, 0.068864195305357756, -0.0038139373727749115, -0.035213776471729075]\n",
            "\n",
            "\n",
            "Processing  Crime\n",
            "[ 0.43283893 -0.89550574  0.32184859  0.23670366 -0.95687812  0.01845984\n",
            "  0.25051172 -0.56081187]\n",
            "Train Accuracy 0.747826086957\n",
            "Train Violation 0.042981126877  \t\t All violations [-0.022066094197241731, 0.0042377606780417132, 0.0030546787496181782, -0.0041916980619760291, 0.0030546787496181782, -0.042981126876980302, -0.00036880635822161834, 0.00015612802498049527]\n",
            "Train Intersect Violations 0.0564272766729  \t All violations [0.019279952783789099, 0.031315111674931763, -0.011736089996959562, 0.030463685536149303, 0.025015770242445157, 0.0216826039851147, -0.037908124240742684, -0.056427276672879063, -0.053875718324433199, -0.03205694561193282, -0.049215175328805628, -0.047190635451505016, 0.019820858801368545, -0.013490903548019767, -0.0082110436147703261, -0.028619206880076443, 0.042809364548494981, 0.026680332290430465, -0.0064330596939292592, -0.046742874257475163, -0.036900780379041244, 0.023201521411240079, 0.042809364548494981]\n",
            "Test Accuracy 0.731462925852\n",
            "Test Violation 0.0833333333333  \t\t All violations [-0.055555555555555552, 0.025073746312684358, -0.022522522522522542, 0.039007092198581561, 0.017543859649122806, -0.083333333333333343, 0.026666666666666644, -0.0061728395061728392]\n",
            "Test Intersect Violations 0.116350347754  \t All violations [0.033453573814295257, 0.024405954766676209, -0.1163503477543322, 0.052055724351929664, 0.047620240480961917, -0.03078885042812899, -0.063475265137015596, 0.018453573814295257, -0.045940365579644142, -0.01987975951903808, -0.034474354113632678, -0.057526818342567489, 0.034479214839936281, -0.011308330947609503, -0.10204192168120024, 0.015344121077976849, 0.060120240480961921, 0.060120240480961921, 0.0074886615335935033, -0.026836281258168511, -0.03333770344427172, 0.03906760890201455, 0.060120240480961921]\n",
            "\n",
            "\n",
            "Processing  German Statlog\n",
            "[-0.09576183]\n",
            "Train Accuracy 0.779104477612\n",
            "Train Violation 0.00621029676685  \t\t All violations [-0.0062102967668531406]\n",
            "Test Accuracy 0.745454545455\n",
            "Test Violation 0.0662423385196  \t\t All violations [-0.066242338519566235]\n",
            "\n",
            "\n"
          ]
        }
      ],
      "source": [
        "for dataset_idx, dataset_name in enumerate(dataset_names):\n",
        "  print(\"Processing \", dataset_name)\n",
        "  X_train, y_train, X_test, y_test, protected_train, protected_test = datas[dataset_idx]\n",
        "  multipliers = np.zeros(len(protected_train))\n",
        "  weights = np.array([1] * X_train.shape[0])\n",
        "  learning_rate = 1.\n",
        "  n_iters = 100\n",
        "  for it in xrange(n_iters):\n",
        "    model = LogisticRegression()\n",
        "\n",
        "    model.fit(X_train, y_train, weights)\n",
        "    y_pred_train = model.predict(X_train)\n",
        "\n",
        "    weights = debias_weights(y_train, y_pred_train, protected_train, multipliers)\n",
        "\n",
        "    acc, violations, pairwise_violations = get_error_and_violations(y_pred_train, y_train, protected_train)\n",
        "    multipliers += learning_rate * np.array(violations)\n",
        "\n",
        "\n",
        "    if (it + 1) % n_iters == 0:\n",
        "      print(multipliers)\n",
        "      y_pred_test = model.predict(X_test)\n",
        "      acc, violations, pairwise_violations = get_error_and_violations(y_pred_train, y_train, protected_train)\n",
        "      print(\"Train Accuracy\", acc)\n",
        "      print(\"Train Violation\", max(np.abs(violations)), \" \\t\\t All violations\", violations)\n",
        "      if len(pairwise_violations) \u003e 0:\n",
        "        print(\"Train Intersect Violations\", max(np.abs(pairwise_violations)), \" \\t All violations\", pairwise_violations)\n",
        "\n",
        "      acc, violations, pairwise_violations = get_error_and_violations(y_pred_test, y_test, protected_test)\n",
        "      print(\"Test Accuracy\", acc)\n",
        "      print(\"Test Violation\", max(np.abs(violations)), \" \\t\\t All violations\", violations)\n",
        "      if len(pairwise_violations) \u003e 0:\n",
        "        print(\"Test Intersect Violations\", max(np.abs(pairwise_violations)), \" \\t All violations\", pairwise_violations)\n",
        "      print()\n",
        "      print()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "//evaluation/analysis/colab/python:rl_colab",
        "kind": "private"
      },
      "name": "Label Bias - EqualOpportunity.ipynb",
      "provenance": [
        {
          "file_id": "1ImucMUz030O2tJgwTeUYH9ep-_bmB0rw",
          "timestamp": 1566864491471
        },
        {
          "file_id": "1SSy4hzElo40ctyS_nfSYvEXBpO4eGbtl",
          "timestamp": 1539411541784
        }
      ],
      "version": "0.3.2"
    },
    "kernelspec": {
      "display_name": "Python 2",
      "name": "python2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
